package com.alibaba.yygh.orders.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.yygh.common.exception.YyghException;
import com.alibaba.yygh.enums.PaymentTypeEnum;
import com.alibaba.yygh.enums.RefundStatusEnum;
import com.alibaba.yygh.model.order.OrderInfo;
import com.alibaba.yygh.model.order.PaymentInfo;
import com.alibaba.yygh.model.order.RefundInfo;
import com.alibaba.yygh.orders.service.OrderInfoService;
import com.alibaba.yygh.orders.service.PaymentInfoService;
import com.alibaba.yygh.orders.service.RefundInfoService;
import com.alibaba.yygh.orders.service.WeixinService;
import com.alibaba.yygh.orders.utils.ConstantPropertiesUtils;
import com.alibaba.yygh.orders.utils.HttpClient;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * description: WeixinServiceImpl
 * @author: gql
 * @date: 2021/11
 */
@Service
public class WeixinServiceImpl implements WeixinService {

    @Autowired
    private OrderInfoService orderInfoService;

    @Autowired
    private PaymentInfoService paymentInfoService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RefundInfoService refundInfoService;

    /**
     * 生成支付二维码
     * @param orderId 订单号
     * @return map集合
     */
    @Override
    public Map createNative(Long orderId) {
        try {
            // 0. 先查询redis中是否有支付信息
            Map payMap = (Map) redisTemplate.opsForValue().get(orderId.toString());
            if (null != payMap) {
                return payMap;
            }

            // 1.根据orderId查询订单信息
            OrderInfo orderInfo = orderInfoService.getOrderInfo(orderId);

            // 2.添加支付记录
            this.paymentInfoService.savePaymentInfo(orderInfo, PaymentTypeEnum.WEIXIN.getStatus());

            // 3.封装数据,调用微信接口得到返回数据(包含二维码地址)
            // 使用map集合封装数据,使用httpclient调用微信接口
            Map paramMap = new HashMap();
            paramMap.put("appid", ConstantPropertiesUtils.APPID);// 公众号id
            paramMap.put("mch_id", ConstantPropertiesUtils.PARTNER);// 商户号
            paramMap.put("nonce_str", WXPayUtil.generateNonceStr());// 随机数

            Date reserveDate = orderInfo.getReserveDate();
            String reserveDateString = new DateTime(reserveDate).toString("yyyy/MM/dd");
            String body = reserveDateString + "就诊" + orderInfo.getDepname();
            paramMap.put("body", body);// 微信扫码之后,微信显示的内容

            paramMap.put("out_trade_no", orderInfo.getOutTradeNo());// 流水号
            // TODO: 上线时使用这行代码
            // paramMap.put("total_fee", order.getAmount().multiply(new BigDecimal("100")).longValue()+"");
            paramMap.put("total_fee", "1");// 支付金额-测试使用0.01

            paramMap.put("spbill_create_ip", "127.0.0.1");// 客户端ip地址
            paramMap.put("notify_url", "http://guli.shop/api/order/weixinPay/weixinNotify");// 回调地址
            paramMap.put("trade_type", "NATIVE");// 支付二维码的类型-NATIVE是带金额

            // 4.发送httpclient请求,请求固定微信接口
            HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
            //      设置并传递参数,以xml格式,根据商户key签名
            client.setXmlParam(WXPayUtil.generateSignedXml(paramMap, ConstantPropertiesUtils.PARTNERKEY));
            client.setHttps(true);// 支持https请求
            client.post();// 发送请求

            // 5.得到微信接口返回的数据
            String xml = client.getContent();
            System.out.println("生成二维码返回值xml格式:" + xml);
            Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);// xml转换为map集合
            // 6.封装返回结果集
            Map map = new HashMap<>();
            map.put("orderId", orderId); // 订单号
            map.put("totalFee", orderInfo.getAmount());// 金额
            map.put("resultCode", resultMap.get("result_code"));// 微信返回的状态码,是随机值
            map.put("codeUrl", resultMap.get("code_url"));// 微信返回的地址

            // 存入Redis中
            if (null != resultMap.get("result_code")) {
                // 微信支付二维码2小时过期，可采取2小时未支付取消订单
                redisTemplate.opsForValue().set(orderId.toString(), map, 2, TimeUnit.HOURS);
            }
            return map;
        } catch (Exception e) {
            throw new YyghException(20001, "生成二维码失败");
        }
    }

    /**
     * 查询支付状态-去微信第三方根据订单号查询
     * @param orderId 订单号
     * @param paymentType 支付类型
     * @return
     */
    @Override
    public Map queryPayStatus(Long orderId, String paymentType) {
        try {
            // 1.根据订单号查询订单信息
            OrderInfo orderInfo = orderInfoService.getOrderInfo(orderId);

            // 2.使用map集合,封装微信接口需要的数据
            Map paramMap = new HashMap<>();
            paramMap.put("appid", ConstantPropertiesUtils.APPID);// 公众号id
            paramMap.put("mch_id", ConstantPropertiesUtils.PARTNER);// 商户号
            paramMap.put("out_trade_no", orderInfo.getOutTradeNo());// 交易流水号
            paramMap.put("nonce_str", WXPayUtil.generateNonceStr());// 随机字符串

            // 3. 调用微信查询支付状态接口
            HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
            //      设置并传递参数,以xml格式,根据商户key签名
            client.setXmlParam(WXPayUtil.generateSignedXml(paramMap, ConstantPropertiesUtils.PARTNERKEY));
            client.setHttps(true);// 支持https请求
            client.post();// 发送请求

            // 5.得到微信接口返回的数据
            String xml = client.getContent();
            System.out.println("生成二维码返回值xml格式:" + xml);
            Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);// xml转换为map集合

            //4、返回
            return resultMap;
        } catch (Exception e) {
            throw new YyghException(20001, "支付失败");
        }
    }

    /**
     * 微信退款
     * @param orderId 订单id
     * @return
     */
    @Override
    public Boolean refund(Long orderId) {
        // TODO 退款记录表相关操作

        // 1.根据订单id查询支付记录
        QueryWrapper<PaymentInfo> wrapperPaymentInfo = new QueryWrapper<>();
        wrapperPaymentInfo.eq("order_id", orderId);
        PaymentInfo paymentInfo = paymentInfoService.getOne(wrapperPaymentInfo);
        if (null == paymentInfo) {
            return false;
        }
        // 添加退款记录
        RefundInfo refundInfo = refundInfoService.saveRefundInfo(paymentInfo);


        // 2.封装微信退款需要的参数,使用map集合
        Map<String, String> paramMap = new HashMap<>(8);
        paramMap.put("appid", ConstantPropertiesUtils.APPID);       //公众账号ID
        paramMap.put("mch_id", ConstantPropertiesUtils.PARTNER);   //商户编号
        paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
        paramMap.put("transaction_id", paymentInfo.getTradeNo()); //微信订单号
        paramMap.put("out_trade_no", paymentInfo.getOutTradeNo()); //商户订单编号
        paramMap.put("out_refund_no", "tk" + paymentInfo.getOutTradeNo()); //商户退款单号
        // TODO: 实际上线时使用下面两行代码:
        //       paramMap.put("total_fee",paymentInfoQuery.getTotalAmount().multiply(new BigDecimal("100")).longValue()+"");
        //       paramMap.put("refund_fee",paymentInfoQuery.getTotalAmount().multiply(new BigDecimal("100")).longValue()+"");
        paramMap.put("total_fee", "1");// 可退款金额
        paramMap.put("refund_fee", "1");// 退款金额, <可退款金额

        // 3.发送httpclient请求,请求微信接口
        HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/secapi/pay/refund");
        try {
            //      设置并传递参数,以xml格式,根据商户key签名
            client.setXmlParam(WXPayUtil.generateSignedXml(paramMap, ConstantPropertiesUtils.PARTNERKEY));
            client.setHttps(true);
            client.setCert(true);// 设置证书
            client.setCertPassword(ConstantPropertiesUtils.PARTNER);// 证书密码是商户号
            client.post();

            // 得到微信接口返回的数据
            String xml = client.getContent();
            System.out.println("退款返回值xml格式:" + xml);
            Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);// xml转换为map集合

            if (null != resultMap && WXPayConstants.SUCCESS.equalsIgnoreCase(resultMap.get("result_code"))) {
                // 修改退款记录为已退款
                refundInfo.setCallbackTime(new Date());
                refundInfo.setTradeNo(resultMap.get("refund_id"));
                refundInfo.setRefundStatus(RefundStatusEnum.REFUND.getStatus());
                refundInfo.setCallbackContent(JSONObject.toJSONString(resultMap));
                refundInfoService.updateById(refundInfo);
                return true;
            }
            return false;
        } catch (Exception e) {
            throw new YyghException(20001, "退款失败");
        }
    }
}
